package com.chb.aspect;

import com.alibaba.fastjson.JSON;
import com.chb.base.pojo.WebLog;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

/**
 * @description:
 * @author: CHB
 * @create: 2021-10-27 15:23
 **/
@Aspect
@Slf4j
@Order(100)
@Component
public class WebLogAspect {
    @Pointcut("within(@org.springframework.web.bind.annotation.RestController *)")
    public void webLog() {
    }

    @Around("webLog()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {

        // 返回 RequestAttributes接口，ServletRequestAttributes --> AbstractRequestAttributes(抽象类) ——> RequestAttributes(接口)
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        try {
            long startTime = System.currentTimeMillis();
            Object result = joinPoint.proceed();
            long endTime = System.currentTimeMillis();
            WebLog webLog = WebLog.builder()
                    .startTime(startTime)
                    .spendTime((int) (endTime - startTime))
                    .ip(request.getRemoteUser())
                    .method(method.getName())
                    .url(request.getRequestURI())
                    .parameter(getParameter(method, joinPoint.getArgs()))
                    .result(result).build();
            log.info("{}",JSON.toJSONString(webLog));

            return result;
        } catch (Throwable throwable) {
            log.warn("请求异常，uri：{}, param：{},message：{}", request.getRequestURI(),
                    JSON.toJSONString(getParameter(method, joinPoint.getArgs())),throwable.getMessage());
            throw throwable;
        }
    }

    /**
     * 获取请求参数
     * @param method
     * @param args
     * @return
     */
    private Object getParameter(Method method,Object[] args){
        ArrayList<Object> argsList = new ArrayList<>();
        Parameter[] parameters = method.getParameters();
//        log.info("method param:{},object args:{}.",method.getParameters(),args);
        for (int i = 0 , len = parameters.length; i < len; i++) {
            Object arg = args[i];
            // 过滤文件参数和空参
            if(arg == null || arg instanceof MultipartFile){
                continue;
            }
            // 获取RequestBody注解标志的参数
            RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class);
            if (requestBody != null){
                Map<String, Object> map = new HashMap<>(16);
                map.put(parameters[i].getName(),arg);
                argsList.add(map);
            }
            // 获取RequestParam标志的参数
            RequestParam requestParam = parameters[i].getAnnotation(RequestParam.class);
            if (requestParam != null){
                Map<String, Object> map = new HashMap<>(16);
                String key = parameters[i].getName();
                if (!StringUtils.isEmpty(requestParam.value())) {
                    key = requestParam.value();
                }
                map.put(key, arg);
                argsList.add(map);
            }
        }
        if(argsList.isEmpty()){
            return  null;
        }else if (argsList.size() == 1){
            return argsList.get(0);
        }else {
            return argsList;
        }
    }
}
